//
// Created by wxd on 2025/9/11.
//

#include "Renderer.h"
#include "../utils/RenderEngineLogger.h"
#include "../glFramework/constant/engine_uniform.h"
#include "../wrapper/monitor/GLDebug.hpp"
#include "../math/matrix/MatrixTransformUtils.hpp"
#include "../glFramework/manager/ShaderManager.h"
#include "demo/material/LightSourceMaterial.h"

Renderer::Renderer() {

}

Renderer::~Renderer() {

}

void Renderer::render(BaseCamera &camera, std::vector<std::reference_wrapper<Mesh> > &meshQueue,
                      AmbientLight &ambientLight, ParallelLight &parallelLight) {
    if (meshQueue.empty()) {
        return;
    }

    // 设置渲染的基础状态机
    GL_CALL(glEnable(GL_DEPTH_TEST))
    GL_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f))
    GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))

    const size_t meshCount = meshQueue.size();

    for (int i = 0; i < meshCount; i++) {
        Mesh &mesh = meshQueue[i];

        auto geometry = mesh.getGeometry();
        auto material = mesh.getMaterial();

        if (!geometry || !material) {
#ifdef LOG_DEBUG
            Log.msg(mesh.getTraceId() + ": geo or material is nullptr").d();
#endif
            continue;
        }

        auto materialType = material->getType();

        if (materialType == MaterialType::PHONG_MATERIAL) {

            auto* phongMaterial = dynamic_cast<PhongMaterial*>(material);

            phongMaterial->bind();

            // MVP矩阵
            phongMaterial->uniform(EngineUniform::uModel, mesh.getModelMatrix());
            phongMaterial->uniform(EngineUniform::uView, camera.getViewMatrix());
            phongMaterial->uniform(EngineUniform::uProj, camera.getProjectMatrix());

            // 法线校正矩阵
            phongMaterial->uniform(EngineUniform::uNormalMat, getNormalMatrix(mesh.getModelMatrix()));

            // 漫反射纹理(激活纹理单元)
            phongMaterial->getDiffuseTexture()->bind();
            phongMaterial->uniform(PhongMaterial::uDiffuseTexture, phongMaterial->getDiffuseTexture()->getTextureUnit());

            // 高光蒙版
            if (phongMaterial->getEnableSpecularMask()) {
                phongMaterial->getSpecularTexture()->bind();
                phongMaterial->uniform(PhongMaterial::uSpecularTexture, phongMaterial->getSpecularTexture()->getTextureUnit());
            }

            // 光源参数
            phongMaterial->uniform(PhongMaterial::uShininess, phongMaterial->getShininess());
            phongMaterial->uniform(AmbientLight::uAmbientColor, ambientLight.getColor());
            phongMaterial->uniform(AmbientLight::uAmbientIntensity, ambientLight.getIntensity());
            phongMaterial->uniform(ParallelLight::uParallelLightColor, parallelLight.getColor());
            phongMaterial->uniform(ParallelLight::uParallelLightDirection, parallelLight.getDirection());
            phongMaterial->uniform(ParallelLight::uParallelLightIntensity, parallelLight.getIntensity());

            // 相机坐标
            phongMaterial->uniform(PhongMaterial::uCameraPos, camera.m_Position);

            // drawCall
            geometry->getVAO()->bind();
            geometry->draw();

            material->unbind();
        }
    }
}

void Renderer::render_V2(BaseCamera &camera, std::vector<std::reference_wrapper<Mesh> > &meshQueue,
                         AmbientLight &ambientLight, ParallelLight &parallelLight) {
    if (meshQueue.empty()) {
        return;
    }

    // 设置渲染的基础状态机
    GL_CALL(glEnable(GL_DEPTH_TEST))
    GL_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f))
    GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))

    const size_t meshCount = meshQueue.size();

    for (int i = 0; i < meshCount; i++) {
        Mesh &mesh = meshQueue[i];

        auto geometry = mesh.getGeometry();
        auto material = mesh.getMaterial();

        if (!geometry || !material) {
#ifdef LOG_DEBUG
            Log.msg(mesh.getTraceId() + ": geo or material is nullptr").d();
#endif
            continue;
        }

        auto materialType = material->getType();
        auto materialId = material->getTraceId();
        auto shader = ShaderManager::getInstance().getShader(materialId);

        if (materialId.empty() || materialType == MaterialType::NONE || !shader) {
            continue;
        }

        if (materialType == MaterialType::PHONG_MATERIAL) {

            auto* phongMaterial = dynamic_cast<PhongMaterial*>(material);

            shader->bindProgram();

            // MVP矩阵
            shader->setUniform(EngineUniform::uModel, mesh.getModelMatrix());
            shader->setUniform(EngineUniform::uView, camera.getViewMatrix());
            shader->setUniform(EngineUniform::uProj, camera.getProjectMatrix());

            // 法线校正矩阵
            shader->setUniform(EngineUniform::uNormalMat, getNormalMatrix(mesh.getModelMatrix()));

            // 漫反射纹理(激活纹理单元)
            phongMaterial->getDiffuseTexture()->bind();
            shader->setUniform(PhongMaterial::uDiffuseTexture, phongMaterial->getDiffuseTexture()->getTextureUnit());

            // 高光蒙版
            if (phongMaterial->getEnableSpecularMask()) {
                phongMaterial->getSpecularTexture()->bind();
                shader->setUniform(PhongMaterial::uSpecularTexture, phongMaterial->getSpecularTexture()->getTextureUnit());
            }

            // 光源参数
            shader->setUniform(PhongMaterial::uShininess, phongMaterial->getShininess());
            shader->setUniform(AmbientLight::uAmbientColor, ambientLight.getColor());
            shader->setUniform(AmbientLight::uAmbientIntensity, ambientLight.getIntensity());
            shader->setUniform(ParallelLight::uParallelLightColor, parallelLight.getColor());
            shader->setUniform(ParallelLight::uParallelLightDirection, parallelLight.getDirection());
            shader->setUniform(ParallelLight::uParallelLightIntensity, parallelLight.getIntensity());

            // 相机坐标
            shader->setUniform(PhongMaterial::uCameraPos, camera.m_Position);

            // drawCall
            geometry->getVAO()->bind();
            geometry->draw();

            shader->unbindProgram();
        }
    }
}

void Renderer::render_V3(BaseCamera &camera, std::vector<std::reference_wrapper<Mesh> > &meshQueue,
                         AmbientLight &ambientLight, PointLight &pointLight) {

    if (meshQueue.empty()) {
        return;
    }

    // 设置渲染的基础状态机
    GL_CALL(glEnable(GL_DEPTH_TEST))
    GL_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f))
    GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))

    const size_t meshCount = meshQueue.size();

    for (int i = 0; i < meshCount; i++) {
        Mesh &mesh = meshQueue[i];

        auto geometry = mesh.getGeometry();
        auto material = mesh.getMaterial();

        if (!geometry || !material) {
#ifdef LOG_DEBUG
            Log.msg(mesh.getTraceId() + ": geo or material is nullptr").d();
#endif
            continue;
        }

        auto materialType = material->getType();
        auto materialId = material->getTraceId();
        auto shader = ShaderManager::getInstance().getShader(materialId);

        if (materialId.empty() || materialType == MaterialType::NONE || !shader) {
            continue;
        }

        if (materialType == MaterialType::PHONG_MATERIAL) {

            auto* phongMaterial = dynamic_cast<PhongMaterial*>(material);

            shader->bindProgram();

            // MVP矩阵
            shader->setUniform(EngineUniform::uModel, mesh.getModelMatrix());
            shader->setUniform(EngineUniform::uView, camera.getViewMatrix());
            shader->setUniform(EngineUniform::uProj, camera.getProjectMatrix());

            // 法线校正矩阵
            shader->setUniform(EngineUniform::uNormalMat, getNormalMatrix(mesh.getModelMatrix()));

            // 漫反射纹理(激活纹理单元)
            phongMaterial->getDiffuseTexture()->bind();
            shader->setUniform(PhongMaterial::uDiffuseTexture, phongMaterial->getDiffuseTexture()->getTextureUnit());

            // 高光蒙版
            if (phongMaterial->getEnableSpecularMask()) {
                phongMaterial->getSpecularTexture()->bind();
                shader->setUniform(PhongMaterial::uSpecularTexture, phongMaterial->getSpecularTexture()->getTextureUnit());
            }

            // 光源参数 (环境光 + 点光源)
            shader->setUniform(PhongMaterial::uShininess, phongMaterial->getShininess());
            shader->setUniform(AmbientLight::uAmbientColor, ambientLight.getColor());
            shader->setUniform(AmbientLight::uAmbientIntensity, ambientLight.getIntensity());
            shader->setUniform(PointLight::uPointLightColor, pointLight.getColor());
            shader->setUniform(PointLight::uPointLightAttenuation, pointLight.getAttenuationCoef());
            shader->setUniform(PointLight::uPointLightPos, pointLight.getPosition());
            shader->setUniform(PointLight::uPointLightIntensity, pointLight.getIntensity());

            // 相机坐标
            shader->setUniform(PhongMaterial::uCameraPos, camera.m_Position);

            // drawCall
            geometry->getVAO()->bind();
            geometry->draw();

            shader->unbindProgram();
        } else if (materialType == MaterialType::LIGHTON_MATERIAL) {
            shader->bindProgram();

            // MVP矩阵
            shader->setUniform(EngineUniform::uModel, mesh.getModelMatrix());
            shader->setUniform(EngineUniform::uView, camera.getViewMatrix());
            shader->setUniform(EngineUniform::uProj, camera.getProjectMatrix());

            geometry->getVAO()->bind();
            geometry->draw();

            shader->unbindProgram();
        }
    }
}

void Renderer::render_V4(BaseCamera &camera, std::vector<std::reference_wrapper<Mesh> > &meshQueue,
                         AmbientLight &ambientLight, SpotLight &spotLight) {
    if (meshQueue.empty()) {
        return;
    }

    // 设置渲染的基础状态机
    GL_CALL(glEnable(GL_DEPTH_TEST))
    GL_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f))
    GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))

    const size_t meshCount = meshQueue.size();

    for (int i = 0; i < meshCount; i++) {
        Mesh &mesh = meshQueue[i];

        auto geometry = mesh.getGeometry();
        auto material = mesh.getMaterial();

        if (!geometry || !material) {
#ifdef LOG_DEBUG
            Log.msg(mesh.getTraceId() + ": geo or material is nullptr").d();
#endif
            continue;
        }

        auto materialType = material->getType();
        auto materialId = material->getTraceId();
        auto shader = ShaderManager::getInstance().getShader(materialId);

        if (materialId.empty() || materialType == MaterialType::NONE || !shader) {
            continue;
        }

        if (materialType == MaterialType::PHONG_MATERIAL) {

            auto* phongMaterial = dynamic_cast<PhongMaterial*>(material);

            shader->bindProgram();

            // MVP矩阵
            shader->setUniform(EngineUniform::uModel, mesh.getModelMatrix());
            shader->setUniform(EngineUniform::uView, camera.getViewMatrix());
            shader->setUniform(EngineUniform::uProj, camera.getProjectMatrix());

            // 法线校正矩阵
            shader->setUniform(EngineUniform::uNormalMat, getNormalMatrix(mesh.getModelMatrix()));

            // 漫反射纹理(激活纹理单元)
            phongMaterial->getDiffuseTexture()->bind();
            shader->setUniform(PhongMaterial::uDiffuseTexture, phongMaterial->getDiffuseTexture()->getTextureUnit());

            // 高光蒙版
            if (phongMaterial->getEnableSpecularMask()) {
                phongMaterial->getSpecularTexture()->bind();
                shader->setUniform(PhongMaterial::uSpecularTexture, phongMaterial->getSpecularTexture()->getTextureUnit());
            }

            // 光源参数 (环境光 + 聚光灯)
            shader->setUniform(PhongMaterial::uShininess, phongMaterial->getShininess());
            shader->setUniform(AmbientLight::uAmbientColor, ambientLight.getColor());
            shader->setUniform(AmbientLight::uAmbientIntensity, ambientLight.getIntensity());
            shader->setUniform(SpotLight::uSpotLightCol, spotLight.getColor());
            shader->setUniform(SpotLight::uSpotLightPos, spotLight.getPosition());
            shader->setUniform(SpotLight::uSpotLightCenterDir, spotLight.getCenterDir());
            shader->setUniform(SpotLight::uSpotLightFovRange, spotLight.getFovRange());

            // 相机坐标
            shader->setUniform(PhongMaterial::uCameraPos, camera.m_Position);

            // drawCall
            geometry->getVAO()->bind();
            geometry->draw();

            shader->unbindProgram();
        } else if (materialType == MaterialType::LIGHTON_MATERIAL) {
            shader->bindProgram();

            // MVP矩阵
            shader->setUniform(EngineUniform::uModel, mesh.getModelMatrix());
            shader->setUniform(EngineUniform::uView, camera.getViewMatrix());
            shader->setUniform(EngineUniform::uProj, camera.getProjectMatrix());

            geometry->getVAO()->bind();
            geometry->draw();

            shader->unbindProgram();
        }
    }
}

void Renderer::render_V5(BaseCamera &camera, std::vector<std::reference_wrapper<Mesh> > &meshQueue, AmbientLight &ambientLight, ParallelLight &parallelLight, PointLight &pointLight, SpotLight &spotLight) {
    if (meshQueue.empty()) {
        return;
    }

    // 设置渲染的基础状态机
    GL_CALL(glEnable(GL_DEPTH_TEST))
    GL_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f))
    GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))

    const size_t meshCount = meshQueue.size();

    for (int i = 0; i < meshCount; i++) {
        Mesh &mesh = meshQueue[i];

        auto geometry = mesh.getGeometry();
        auto material = mesh.getMaterial();

        if (!geometry || !material) {
#ifdef LOG_DEBUG
            Log.msg(mesh.getTraceId() + ": geo or material is nullptr").d();
#endif
            continue;
        }

        auto materialType = material->getType();
        auto materialId = material->getTraceId();
        auto shader = ShaderManager::getInstance().getShader(materialId);

        if (materialId.empty() || materialType == MaterialType::NONE || !shader) {
            continue;
        }

        if (materialType == MaterialType::PHONG_MATERIAL) {

            auto* phongMaterial = dynamic_cast<PhongMaterial*>(material);

            shader->bindProgram();

            shader->setUniform(EngineUniform::uModel, mesh.getModelMatrix());
            shader->setUniform(EngineUniform::uView, camera.getViewMatrix());
            shader->setUniform(EngineUniform::uProj, camera.getProjectMatrix());

            shader->setUniform(EngineUniform::uNormalMat, getNormalMatrix(mesh.getModelMatrix()));

            phongMaterial->getDiffuseTexture()->bind();
            shader->setUniform(PhongMaterial::uDiffuseTexture, phongMaterial->getDiffuseTexture()->getTextureUnit());

            if (phongMaterial->getEnableSpecularMask()) {
                phongMaterial->getSpecularTexture()->bind();
                shader->setUniform(PhongMaterial::uSpecularTexture, phongMaterial->getSpecularTexture()->getTextureUnit());
            }

            shader->setUniform(PhongMaterial::uShininess, phongMaterial->getShininess());

            // 光源参数传递
            shader->setUniform(AmbientLight::uAmbientColor, ambientLight.getColor());
            shader->setUniform(AmbientLight::uAmbientIntensity, ambientLight.getIntensity());
            shader->setUniform(ParallelLight::uParallelLightColor, parallelLight.getColor());
            shader->setUniform(ParallelLight::uParallelLightDirection, parallelLight.getDirection());
            shader->setUniform(ParallelLight::uParallelLightIntensity, parallelLight.getIntensity());
            shader->setUniform(PointLight::uPointLightColor, pointLight.getColor());
            shader->setUniform(PointLight::uPointLightAttenuation, pointLight.getAttenuationCoef());
            shader->setUniform(PointLight::uPointLightPos, pointLight.getPosition());
            shader->setUniform(PointLight::uPointLightIntensity, pointLight.getIntensity());
            shader->setUniform(SpotLight::uSpotLightCol, spotLight.getColor());
            shader->setUniform(SpotLight::uSpotLightPos, spotLight.getPosition());
            shader->setUniform(SpotLight::uSpotLightCenterDir, spotLight.getCenterDir());
            shader->setUniform(SpotLight::uSpotLightFovRange, spotLight.getFovRange());

            shader->setUniform(PhongMaterial::uCameraPos, camera.m_Position);

            geometry->getVAO()->bind();
            geometry->draw();

            shader->unbindProgram();
        } else if (materialType == MaterialType::LIGHTON_MATERIAL) {
            shader->bindProgram();

            uniformMvp(*shader, mesh.getModelMatrix(), camera.getViewMatrix(), camera.getProjectMatrix());

            geometry->getVAO()->bind();
            geometry->draw();

            shader->unbindProgram();
        }
    }
}


void Renderer::uniformMvp(Shader &shader, const glm::mat4& m, const glm::mat4& v, const glm::mat4& p) {
    shader.setUniform(EngineUniform::uModel, m);
    shader.setUniform(EngineUniform::uView, v);
    shader.setUniform(EngineUniform::uProj, p);
}

/*void Renderer::uniformLight(Shader &shader, ILight *light) {
    switch (LightType type = light->getType()) {
        case LightType::Ambient:
            auto ambientLight = dynamic_cast<AmbientLight*>(light);
            shader.setUniform(AmbientLight::uAmbientColor, ambientLight->getColor());
            shader.setUniform(AmbientLight::uAmbientIntensity, ambientLight->getIntensity());
            break;
        case LightType::Parallel:
            auto parallelLight = dynamic_cast<ParallelLight*>(light);
            shader.setUniform(ParallelLight::uParallelLightColor, parallelLight->getColor());
            shader.setUniform(ParallelLight::uParallelLightDirection, parallelLight->getDirection());
            shader.setUniform(ParallelLight::uParallelLightIntensity, parallelLight->getIntensity());
            break;
        case LightType::Point:
            auto pointLight = dynamic_cast<PointLight*>(light);
            shader.setUniform(PointLight::uPointLightColor, pointLight->getColor());
            shader.setUniform(PointLight::uPointLightAttenuation, pointLight->getAttenuationCoef());
            shader.setUniform(PointLight::uPointLightPos, pointLight->getPosition());
            shader.setUniform(PointLight::uPointLightIntensity, pointLight->getIntensity());
            break;
        case LightType::Spot:
            auto spotLight = dynamic_cast<SpotLight*>(light);
            shader.setUniform(SpotLight::uSpotLightCol, spotLight->getColor());
            shader.setUniform(SpotLight::uSpotLightPos, spotLight->getPosition());
            shader.setUniform(SpotLight::uSpotLightCenterDir, spotLight->getCenterDir());
            shader.setUniform(SpotLight::uSpotLightFovRange, spotLight->getFovRange());
            break;
        default:
            break;
    }
}*/
